class_name SteeringComponent
extends EntityControllerComponent

#region 属性
@export var update_dalay:float = 0.02
@export var resolution_radio:int = 12
@onready var fsm: FSM = %FSM
var cur_speed = 0
var context_behaviors:Array[BaseContextBehavior] = []
var directions:Array[Vector2] = []
var interests:Array[float] = []
var dangers:Array[float] = []
var interestss:Array[Array] = []
var dangerss:Array[Array] = []
var update_timer:Timer
#region ExpressionProperty
# 无后缀的为属性，可以直接访问
# 后缀为_trigger的，要求为bool类型，用于TriggerGuard，可以在访问后自动重置回false
# 后缀为_func的，是一个方法名，使用时需要主动调用以访问结果
const expression_property_name_dict:Dictionary = {
	cur_health = "cur_health", # 当前生命值
	cur_health_state = "cur_health_state", # 当前生命状态
	on_hit = "on_hit_trigger", # 是否受到伤害 false-未受伤，true-受伤，这个属性需要手动重置
	on_recover = "on_recover_trigger", # 受否受到治疗 false-未治疗，true-治疗，这个属性需要手动重置
	on_target_count_greater = "on_target_count_greater_func", # 目标数量 args：数量，范围，是否可见，数量是否取反，区域索引列表，是否需要全部检测到
	on_camp_count_greater = "on_camp_count_greater_func", # 同阵营数量 args：数量，范围，数量是否取反
	on_target_in_area = "on_target_in_area_func", # 在指定区域里的目标数量 args：区域索引列表，是否可见，是否需要全部检测到，最小数量=1
	on_camp_in_area = "on_camp_in_area_func", # 在指定区域里的同阵营数量 args：区域索引列表，是否需要全部检测到，最小数量=1
}
#endregion
#endregion
#region 字段
#endregion
#region 信号
#endregion

#region 基类方法
func init_component(entity):
	super.init_component(entity)
	_init_directions()
	for child in get_children():
		if child is BaseContextBehavior:
			child.init_context(self,resolution_radio)
			context_behaviors.append(child)
	control_all_context_behavior(false)
	update_timer = Timer.new()
	update_timer.wait_time = update_dalay
	update_timer.one_shot = false
	update_timer.timeout.connect(_update)
	update_timer.autostart = true
	add_child(update_timer)
	var health_component = cur_entity.get_component_by_name("HealthComponent") as HealthComponent
	health_component.on_cur_health_changed.connect(func (cur_health,cur_max_health):
		set_expression_property(expression_property_name_dict.cur_health,cur_health)
		)
	health_component.on_cur_health_state_changed.connect(func (new_health_state):
		set_expression_property(expression_property_name_dict.cur_health_state,new_health_state)
		)
	health_component.on_hit.connect(func (cur_health):
		set_expression_property(expression_property_name_dict.on_hit,true)
		)
	health_component.on_recover.connect(func (cur_health):
		set_expression_property(expression_property_name_dict.on_recover,true)
		)
	fsm.init_fsm(self)
func set_target_vel(delta:float):
	target_vel = get_steering() * cur_entity.get_entity_data().max_speed
#endregion
#region 公共方法
func get_steering() -> Vector2:
	var final_dir:Vector2 = Vector2.ZERO
	for i in range(directions.size()):
		final_dir += (interests[i] - dangers[i]) * directions[i]
	return final_dir.normalized()
func control_context_behavior_by_type(type:GameEnum.ContextBehaviorType,valid:bool):
	for behavior in context_behaviors.filter(func (b):return b.context_behavior_type & type > 0):
		behavior.enable = valid
func control_all_context_behavior(valid:bool):
	for behavior in context_behaviors:
		behavior.enable = valid
func translate_state(state_name:String):
	fsm.translate_state_by_state_name(state_name)
func set_expression_property(name:StringName,value):
	fsm.set_expression_property(name,value)
func on_target_count_greater_func(count:int,radius:float = -1,is_visible:bool = false,is_inversion:bool = false,area2D_detector_ids:Array[int] = [],is_all = false) -> bool:
	var detect_component = cur_entity.get_component_by_name("DetectComponent") as DetectComponent
	var target_detector:TargetDetector = detect_component.get_detector(GameEnum.DetectorType.Target) as TargetDetector
	var targets:Array = []
	if is_visible:
		targets = target_detector.visible_entities
	else:
		targets = target_detector.entities
	if radius >= 0:
		targets = targets.filter(func (x):
			return (x.global_position - target_detector.global_position).length() <= radius
		)
	targets = targets.filter(func (x):
		var amount = 0
		for id in area2D_detector_ids:
			var area2d = detect_component.get_area2d(id)
			if area2d:
				amount += 1 if area2d.overlaps_body(x.logic_body_area) else 0
		return amount == area2D_detector_ids.size() if is_all else (amount > 0 or area2D_detector_ids.size() == 0)
	)
	return targets.size() <= count if is_inversion else targets.size() > count
func on_camp_count_greater_func(count:int,radius:float = -1,is_inversion:bool = false,area2D_detector_ids:Array[int] = [],is_all = false) -> bool:
	var camp_component = cur_entity.get_component_by_name("CampComponent") as CampComponent
	var detect_component = cur_entity.get_component_by_name("DetectComponent") as DetectComponent
	var targets:Array = GameCampSystem.get_target(camp_component.get_camp_camps(),cur_entity.global_position,false,-1,radius)
	targets = targets.filter(func (x):
		var amount = 0
		for id in area2D_detector_ids:
			var area2d = detect_component.get_area2d(id)
			if area2d:
				amount += 1 if area2d.overlaps_body(x.logic_body_area) else 0
		return amount == area2D_detector_ids.size() if is_all else (amount > 0 or area2D_detector_ids.size() == 0)
	)
	return targets.size() <= count if is_inversion else targets.size()
func on_target_in_area_func(area2D_detector_ids:Array[int],is_visible:bool = false,is_all = false,min_count = 1) -> bool:
	var component:Area2DDetectComponent = cur_entity.get_component_by_name("Area2DDetectComponent") as Area2DDetectComponent
	var detect_component = cur_entity.get_component_by_name("DetectComponent") as DetectComponent
	var target_detector:TargetDetector = detect_component.get_detector(GameEnum.DetectorType.Target) as TargetDetector
	var targets:Array = []
	if is_visible:
		targets = target_detector.visible_entities
	else:
		targets = target_detector.entities
	targets = targets.filter(func (t):
		var amount = 0
		for id in area2D_detector_ids:
			var area2d = component.get_area2d(id)
			if area2d:
				amount += 1 if area2d.overlaps_body(t.logic_body_area) else 0
		return amount == area2D_detector_ids.size() if is_all else (amount > 0 or area2D_detector_ids.size() == 0)
		)
	return targets.size() >= min_count
func on_camp_in_area_func(area2D_detector_ids:Array[int],is_all = false,min_count = 1) -> bool:
	var component:Area2DDetectComponent = cur_entity.get_component_by_name("Area2DDetectComponent") as Area2DDetectComponent
	var camp_component = cur_entity.get_component_by_name("CampComponent") as CampComponent
	var targets:Array = GameCampSystem.get_target(camp_component.get_camp_camps(),cur_entity.global_position)
	targets = targets.filter(func (t):
		var amount = 0
		for id in area2D_detector_ids:
			var area2d = component.get_area2d(id)
			if area2d:
				amount += 1 if area2d.overlaps_body(t.logic_body_area) else 0
		return amount == area2D_detector_ids.size() if is_all else (amount > 0 or area2D_detector_ids.size() == 0)
		)
	return targets.size() >= min_count
#endregion
#region 私有方法
func _init_directions():
	for i in range(resolution_radio):
		var rad = TAU/resolution_radio*i
		directions.append(Vector2(cos(rad),sin(rad)))
	interests = []
	interests.resize(resolution_radio)
	dangers = []
	dangers.resize(resolution_radio)
	interestss = []
	dangerss = []
func _update():
	_update_interest_directions()
	_update_danger_directions()
func _update_interest_directions():
	interests = []
	interests.resize(resolution_radio)
	interestss.clear()
	for behavior in context_behaviors:
		if behavior.enable:
			interestss.append(behavior.get_interest(cur_entity,directions))
	for cur_interest in interestss:
		for i in range(cur_interest.size()):
			interests[i] += cur_interest[i]
func _update_danger_directions():
	dangers = []
	dangers.resize(resolution_radio)
	dangerss.clear()
	for behavior in context_behaviors:
		if behavior.enable:
			dangerss.append(behavior.get_danger(cur_entity,directions))
	for cur_danger in dangerss:
		for i in range(cur_danger.size()):
			dangers[i] += cur_danger[i]
#endregion
#region 生命周期函数
func _process(delta: float) -> void:
	super._process(delta)
	if cur_entity:
		cur_entity.velocity = get_target_vel()
		cur_entity.move_and_slide()
#endregion
